using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Xml;

namespace SqlTools.CodeGenerator.Data
{
	public enum TablePropertyType
	{
		Intrinsic,
		ForeignKey,
		PrimaryKey,
		Temp
	}

	public abstract class TableProperty
	{
		protected string _propertyName;
		protected Table _table;
		protected string _columnName;
		protected string _csharpDataType;
		protected string _fieldName;
		protected bool _nullable;
		protected string _defaultValue;
		protected bool _autoGeneratedOnInsert;
		protected int _dataLength;

		protected SqlDbType _datatype;

		protected List<ForeignKeyTableProperty> _foreignKeys;

		public TableProperty(string name, Table table, string columnName)
		{
			_propertyName = name;
			_table = table;
			_columnName = columnName;

			_csharpDataType = null;
			_fieldName = "";
			_nullable = false;
			_defaultValue = null;
			_autoGeneratedOnInsert = false;
			_dataLength = -1;

			_datatype = SqlDbType.Int;

			_foreignKeys = new List<ForeignKeyTableProperty>();
		}

		public string PropertyName
		{
			get { return _propertyName; }
			set { _propertyName = value; }
		}

		public Table Table
		{
			get { return _table; }
		}

		public string ColumnName
		{
			get { return _columnName; }
		}

		public abstract TablePropertyType Type
		{
			get;
		}

		public string CSharpDataType
		{
			get
			{
				if ( _csharpDataType == null )
					_csharpDataType = GetCSharpDatatype();

				return _csharpDataType;
			}
		}

		public SqlDbType DataType
		{
			get { return _datatype; }
			set { _datatype = value; }
		}

		public string FieldName
		{
			get { return _fieldName; }
			set { _fieldName = value; }
		}

		public bool Nullable
		{
			get { return _nullable; }
			set { _nullable = value; }
		}

		public string DefaultValue
		{
			get
			{
				if ( _defaultValue == null )
					_defaultValue = GetDefaultValue();

				return _defaultValue;
			}
			set { _defaultValue = value; }
		}

		public bool AutoGeneratedOnInsert
		{
			get { return _autoGeneratedOnInsert; }
			set { _autoGeneratedOnInsert = value; }
		}

		public int DataLength
		{
			get { return _dataLength; }
			set { _dataLength = value; }
		}

		public List<ForeignKeyTableProperty> ForeignKeys
		{
			get { return _foreignKeys; }
		}

		private string GetCSharpDatatype()
		{
			string nullable = ( _nullable ? "?" : "" );

			if ( this.Type == TablePropertyType.ForeignKey )
				nullable = "";

			switch ( _datatype )
			{
				case SqlDbType.BigInt:
					return "long" + nullable;
				case SqlDbType.Binary:
					return "byte[]";
				case SqlDbType.Bit:
					return "bool" + nullable;
				case SqlDbType.Char:
					return "string";
				case SqlDbType.DateTime:
					return "DateTime" + nullable;
				case SqlDbType.Decimal:
					return "decimal" + nullable;
				case SqlDbType.Float:
					return "double" + nullable;
				case SqlDbType.Image:
					return "byte[]";
				case SqlDbType.Int:
					return "int" + nullable;
				case SqlDbType.Money:
					return "decimal" + nullable;
				case SqlDbType.NChar:
					return "string";
				case SqlDbType.NText:
					return "string";
				case SqlDbType.NVarChar:
					return "string";
				case SqlDbType.Real:
					return "float" + nullable;
				case SqlDbType.SmallDateTime:
					return "DateTime" + nullable;
				case SqlDbType.SmallInt:
					return "short" + nullable;
				case SqlDbType.SmallMoney:
					return "decimal" + nullable;
				case SqlDbType.Text:
					return "string";
				case SqlDbType.Timestamp:
					return "byte[]";
				case SqlDbType.TinyInt:
					return "byte" + nullable;
				case SqlDbType.Udt:
					return "Object";
				case SqlDbType.UniqueIdentifier:
					return "Guid";
				case SqlDbType.VarBinary:
					return "byte[]";
				case SqlDbType.VarChar:
					return "string";
				case SqlDbType.Variant:
					return "Object";
				case SqlDbType.Xml:
					return "XmlDocument";
			}

			return "Object";
		}

		private string GetDefaultValue()
		{
			if ( _nullable && this.Type != TablePropertyType.ForeignKey )
				return "null";

			switch ( _datatype )
			{
				case SqlDbType.BigInt:
					return "0";
				case SqlDbType.Binary:
					return "null";
				case SqlDbType.Bit:
					return "false";
				case SqlDbType.Char:
					return "\"\"";
				case SqlDbType.DateTime:
					return "DateTime.MinValue";
				case SqlDbType.Decimal:
					return "0.0m";
				case SqlDbType.Float:
					return "0.0d";
				case SqlDbType.Image:
					return "null";
				case SqlDbType.Int:
					return "0";
				case SqlDbType.Money:
					return "0.0m";
				case SqlDbType.NChar:
					return "\"\"";
				case SqlDbType.NText:
					return "\"\"";
				case SqlDbType.NVarChar:
					return "\"\"";
				case SqlDbType.Real:
					return "0.0f";
				case SqlDbType.SmallDateTime:
					return "DateTime.MinValue";
				case SqlDbType.SmallInt:
					return "0";
				case SqlDbType.SmallMoney:
					return "0.0m";
				case SqlDbType.Text:
					return "\"\"";
				case SqlDbType.Timestamp:
					return "null";
				case SqlDbType.TinyInt:
					return "0";
				case SqlDbType.Udt:
					return "null";
				case SqlDbType.UniqueIdentifier:
					return "Guid.Empty";
				case SqlDbType.VarBinary:
					return "null";
				case SqlDbType.VarChar:
					return "\"\"";
				case SqlDbType.Variant:
					return "null";
				case SqlDbType.Xml:
					return "null";

			}

			return "null";
		}

		public virtual void SaveXml(XmlDocument doc, XmlElement parent)
		{
			XmlElement el = CreateXmlElement(doc);
			parent.AppendChild(el);
		}

		public virtual XmlElement CreateXmlElement(XmlDocument doc)
		{
			XmlElement xmlProp = doc.CreateElement("Property");

			xmlProp.Attributes.Append(doc.CreateAttribute("Type")).Value = Type.ToString();
			xmlProp.Attributes.Append(doc.CreateAttribute("PropertyName")).Value = _propertyName;
			xmlProp.Attributes.Append(doc.CreateAttribute("ColumnName")).Value = _columnName;
			xmlProp.Attributes.Append(doc.CreateAttribute("Datatype")).Value = _datatype.ToString();
			xmlProp.Attributes.Append(doc.CreateAttribute("FieldName")).Value = _fieldName;
			xmlProp.Attributes.Append(doc.CreateAttribute("Nullable")).Value = _nullable.ToString();
			if ( _defaultValue != null )
				xmlProp.Attributes.Append(doc.CreateAttribute("DefaultValue")).Value = _defaultValue;
			xmlProp.Attributes.Append(doc.CreateAttribute("AutoGeneratedOnInsert")).Value = _autoGeneratedOnInsert.ToString();
			xmlProp.Attributes.Append(doc.CreateAttribute("DataLength")).Value = _dataLength.ToString();


			return xmlProp;
		}

		public virtual void LoadXml(XmlElement xmlProp)
		{
			_propertyName = xmlProp.GetAttribute("PropertyName");
			_columnName = xmlProp.GetAttribute("ColumnName");

			string typename = xmlProp.GetAttribute("Datatype");
			_datatype = (SqlDbType)Enum.Parse(typeof(SqlDbType), typename);

			_fieldName = xmlProp.GetAttribute("FieldName");

			bool.TryParse(xmlProp.GetAttribute("Nullable"), out _nullable);

			string defval = xmlProp.GetAttribute("DefaultValue");
			if ( !string.IsNullOrEmpty(defval) )
				_defaultValue = defval;

			bool.TryParse(xmlProp.GetAttribute("AutoGeneratedOnInsert"), out _autoGeneratedOnInsert);

			if ( !int.TryParse(xmlProp.GetAttribute("DataLength"), out _dataLength) )
				_dataLength = -1;
		}

		public override string ToString()
		{
			return _columnName;
		}
	}
}
